package org.zhixiang.gisQuery.query;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.io.FileUtils;
import org.zhixiang.gisQuery.rtree.DoCheckInRegio;
import org.zhixiang.gisQuery.rtree.Point;
import org.zhixiang.gisQuery.rtree.RTree;
import org.zhixiang.gisQuery.rtree.Rectangle;
import org.zhixiang.gisQuery.utils.JacksonUtil;

/**
 * 
 * @say little Boy, don't be sad.
 * @name Rezar
 * @time 2018年9月14日 下午3:48:00
 * @Desc 些年若许,不负芳华.
 *
 */
public class GPSLocator {

	public static final String GIS_ENDFIX = "_gis.json";

	private RTree tree;
	private final Map<Integer, RectangleMBR> mbrCache = new HashMap<>();
	private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");

	public GPSLocator() throws IOException {
		tree = new RTree();
		tree.init(null);
		URL resource = GPSLocator.class.getClassLoader().getResource("gisDatas");
		try {
			System.out.println(resource.toURI().getPath());
			initPoiDatas(resource.toURI().getPath());
			System.out.println("load over:" + format.format(new Date()));
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}

	public GPSLocator(String gisDataDir) throws IOException {
		tree = new RTree();
		tree.init(null);
		System.out.println("load begin:" + format.format(new Date()));
		initPoiDatas(gisDataDir);
		System.out.println("load over:" + format.format(new Date()));
	}

	public GPSLocator(String polyline, String cityCode, String cityName) throws IOException {
		tree = new RTree();
		tree.init(null);
		List<RectangleMBR> parseRectangle = RectangleMbrParser.parseRectangle(polyline, cityCode, cityName);
		int mbrIndex = 1;
		for (RectangleMBR mbr : parseRectangle) {
			addAreaItem(mbr, mbrIndex++);
		}
		System.out.println("load over:" + format.format(new Date()));
	}

	/**
	 * @param mbrCache
	 * @param tree
	 * @param format
	 * @return
	 */
	public RectangleMBR doQuery(String queryPoi) {
		if (queryPoi == null || queryPoi.isEmpty()) {
			return null;
		}
		long time1 = System.currentTimeMillis();
		String[] split = queryPoi.split(",");
		final double lon = Double.parseDouble(split[1]);
		final double lat = Double.parseDouble(split[0]);
		SelfPoint originPoint = new SelfPoint(lon, lat);
		final Point p = new Point(originPoint.x, originPoint.y);
		final AtomicInteger respMbr = new AtomicInteger(-1);
		tree.findInside(p, new DoCheckInRegio() {
			@Override
			public boolean doCheck(int mbrId) {
				// totalCount.incrementAndGet();
				RectangleMBR mbr = mbrCache.get(mbrId);
				boolean checkInBound = mbr.checkInRange(p);
				if (checkInBound) {
					respMbr.set(mbrId);
				}
				return checkInBound;
			}
		});
		long time2 = System.currentTimeMillis();
		long userTime = (time2 - time1);
		RectangleMBR resp = respMbr.get() == -1 ? null : this.mbrCache.get(respMbr.get());
		System.out.println(
				"query :" + queryPoi + "\tmbr:" + (resp == null ? "无结果" : resp.getCity()) + "\tuserTime:" + userTime);
		return resp;
	}

	public static int useTime = 0;
	public static int maxCheckCount = 0;

	/**
	 * @param mbrCache
	 * @param tree
	 * @param format
	 * @return
	 */
	public RectangleMBR doQuery(final double lon, final double lat) {
		final Point p = new Point(lon, lat);
		final AtomicInteger respMbr = new AtomicInteger(-1);
		// final AtomicInteger totalCount = new AtomicInteger(0);
		tree.findInside(p, new DoCheckInRegio() {
			@Override
			public boolean doCheck(int mbrId) {
				// totalCount.incrementAndGet();
				RectangleMBR mbr = mbrCache.get(mbrId);
				boolean checkInBound = mbr.checkInRange(p);
				if (checkInBound) {
					respMbr.set(mbrId);
				}
				return checkInBound;
			}
		});
		// if (totalCount.get() > maxCheckCount) {
		// maxCheckCount = totalCount.get();
		// }
		return respMbr.get() == -1 ? null : this.mbrCache.get(respMbr.get());
	}

	/**
	 * @param mbrCache
	 * @param tree
	 * @throws IOException
	 */
	private void initPoiDatas(String gisDataDirStr) throws IOException {
		File gisDataDir = new File(gisDataDirStr);
		if (!gisDataDir.exists() || !gisDataDir.isDirectory()) {
			System.out.println("init failure , " + gisDataDirStr + " not exists or not a dir");
			return;
		}
		File[] listFiles = gisDataDir.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(GIS_ENDFIX);
			}
		});
		int mbrIndex = 1;
		for (File file : listFiles) {
			String readFileToString = FileUtils.readFileToString(file);
			List<RectangleMBR> parseDis = JacksonUtil.str2List(readFileToString, RectangleMBR.class); // GaoDeJsonDataParser.parseJsonDis2();
			for (RectangleMBR mbr : parseDis) {
				addAreaItem(mbr, mbrIndex++);
			}
		}
	}

	/**
	 * @param mbr
	 */
	protected void configMbr(RectangleMBR mbr) {
		List<SelfPoint> polyline = mbr.getPolyline();
		double minX = Double.MAX_VALUE;
		double maxX = Double.MIN_VALUE;
		double minY = Double.MAX_VALUE;
		double maxY = Double.MIN_VALUE;
		for (SelfPoint selfPoint : polyline) {
			if (selfPoint.x < minX) {
				minX = selfPoint.x;
			}
			if (selfPoint.x > maxX) {
				maxX = selfPoint.x;
			}
			if (selfPoint.y < minY) {
				minY = selfPoint.y;
			}
			if (selfPoint.y > maxY) {
				maxY = selfPoint.y;
			}
		}
		Rectangle re = new Rectangle(minX, minY, maxX, maxY);
		mbr.setRectangle(re);
		mbr.initMinBound(150, 150);
	}

	/**
	 * @param mbr
	 */
	private void addAreaItem(RectangleMBR mbr, int index) {
		mbr.setMbrId(index);
		mbrCache.put(index, mbr);
		tree.add(mbr.getRectangle(), index);
	}

}
